Jerry Greenough

jerry.greenough@jgxsoft.com
www.jgxsoft.com



Aspects of Today's Fortran

The Fortran language has advanced steadily since the days of Fortran IV. In fact, many of those who were active in Fortran programming during the 1970's have difficulty in even recognizing today's constructs as Fortran code. The development of the Fortran language has been driven by the need to (i) incorporate modern programming paradigms that encourage the generation of robust and readable source code and (ii) meet the needs of high performance computing - and yet still provide a language that is simple to learn, and moreover a language which generally engenders fast and efficient computation.

Fortran 90 brought about some important enhancements to the Fortran language. This included the ability to reserve non-stack memory through the use of dynamic arrays as well as a means to encapsulate functionality by means of a 'module'. Powerful processing of array sections was introduced along with constructs for handling procedure interfaces. It was also at this stage that the programmer was given the option to write in free form, i.e. without limiting the placement of Fortran keywords to columns 7 through 72.

Fortran 2003 heralded the advent of formal constructs for object-oriented programming in Fortran. Procedure pointers were also introduced as well as techniques for interoperability with C. A standard procedure for interfacing Fortran and C reduces the number of compiler directives required for multi-platform and/or multi-compiler source code.

Today's Fortran offers the programmer many surprising capabilities that would have been challenging, if not impossible to implement in Fortran 77. Here are some (non-exhaustive) snippets/ideas that illustrate the possibilities of the language in its modern form.

_. Operator Overloading
_. Type-bound Procedures


Operator Overloading

This demonstrates a simple programmer-defined cross product operator written in Fortran 95. The cross (or vector) product takes as inputs two vectors of dimension 3, $ \boldsymbol{v_1} $ and $ \boldsymbol{v_2} $ and outputs another vector of dimension 3, $ \boldsymbol{v_3} $ according to the familiar formula: $$ \boldsymbol{v_3} = \boldsymbol{v_1} \times \boldsymbol{v_2} = \begin{vmatrix} \boldsymbol{i}&\boldsymbol{j}&\boldsymbol{k} \\ v_{1x}&v_{1y}&v_{1z} \\ v_{2x}&v_{2y}&v_{2z} \end{vmatrix} $$ Note that the above formula is implemented by the function CrossProductR3 defined inside the module TYPES, whose definition is listed to the right.

The cross product operator defined here provides an example of operator overloading. It also shows that the operator token (in this case .cross.) need not be one of the traditional numeric or relational operators such as '*' or ' .and.'. In fact, it can be any combination of up to 31 letters (delimited by a period '.'), with the exception of a couple of commonly used constants.

The operator itself is defined inside a module with an interface block. In this instance, the purpose of the interface block is to associate the calculation function, CrossProductR3 with the operator token .cross.. The .cross. operator is easy to use, is very readable and is intuitively close to the semantics of vector operators in mathematics.

USE TYPES
REAL(KIND=dp), DIMENSION(3) :: V1, V2, V3
V1 = (/ 1.0_dp, 0.0_dp, 0.0_dp /)
V2 = (/ 0.0_dp, 1.0_dp, 0.0_dp /)
V3 = V1 .cross. V2

Back to Aspects of Today's Fortran


Type-bound Procedures

A type-bound procedure can be thought of as analgous to the member function of a C++ class. There are in fact devices in Fortran 2003 for overriding the type-bound procedures of a base type with a type-bound procedure in an extended type, in much the same way that an object of a derived class might have a member function that overrides a member function of the base class.

Here is a simple example of a type-bound procedure. An Ellipse derived type is defined inside the module EllipseDef (see right). The Ellipse type defines components for a name, a color, the resolution to be used for rendering and minor- and major- axis lengths. The objective here is to define a type-bound procedure getName() that will return an ellipse's name without having to directly interface with the Ellipse derived type's components.

The getName() function itself is defined as a module procedure underneath the second CONTAINS keyword. Fortran 2003 assumes (by default) that the first argument passed to a type-bound procedure is an instance of the derived type itself. In the current example the first argument of the getName() function is THIS, which contains the information about the ellipse for which the getName() function is being invoked.

The PROCEDURE keyword (underneath the first CONTAINS keyword) is there to bind the module's getName() function to the Ellipse type. The following code snippet shows the 'construction' of an Ellipse record and subsequent use of getName() to retrieve the name of the ellipse ELL1 using semantics enabled by Fortran 2003.

USE EllipseDef
IMPLICIT NONE
TYPE(Ellipse) ELL1

ELL1 = Ellipse('Ellipse 1',2, 20, 1.0, 2.0)

WRITE(*,*) ELL1%getName()

Back to Aspects of Today's Fortran


Jerry Greenough
MODULE TYPES

INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15, 307)

INTERFACE OPERATOR (.cross.)
MODULE PROCEDURE CrossProductR3
END INTERFACE

CONTAINS

FUNCTION CrossProductR3(V1, V2)
REAL(KIND=dp), DIMENSION(3) :: CrossProductR3
REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: V1, V2

CrossProductR3(1) = V1(2) * V2(3) - V2(2) * V1(3)
CrossProductR3(2) = V2(1) * V1(3) - V1(1) * V2(3)
CrossProductR3(3) = V1(1) * V2(2) - V2(1) * V1(2)

END FUNCTION CrossProductR3
END MODULE TYPES
MODULE EllipseDef

TYPE Ellipse
CHARACTER(LEN=16) name
INTEGER color
INTEGER numRenderSegments
REAL minorAxisLength
REAL majorAxisLength
CONTAINS
PROCEDURE :: getName
END TYPE Ellipse

CONTAINS

FUNCTION getName(THIS) RESULT(nm)
CLASS(Ellipse), INTENT(IN) :: THIS
CHARACTER(LEN=16) nm
nm = THIS%name
END FUNCTION getName

END MODULE EllipseDef

Copyright © 2018 JGX Software Solutions LLC. All Rights Reserved.